Graph capability repairs — the 0.7.0 seams, simulation-verified#224
Closed
qwerfunch wants to merge 11 commits into
Closed
Graph capability repairs — the 0.7.0 seams, simulation-verified#224qwerfunch wants to merge 11 commits into
qwerfunch wants to merge 11 commits into
Conversation
…e binary to git src/graph/model.ts (edge-dedup key), src/optimizer/infer-depends-on.ts (edge-map key), src/spec/attestation.ts (sha256 separators) carried literal 0x00 chars inside string/template literals. Git's binary heuristic then hid those files from every diff/blame/review — the 0.7.0 graph core shipped as "Bin 0 -> 9363 bytes" with a review-invisible diff. The escape spelling is byte-identical at runtime (attestation digests unchanged; verified by simulation before the change). Adds a self-consistency test banning raw NUL bytes across the source tree so a file can never silently become review-invisible again. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
…id lexer doc-references.ts matched only 6-8 hex ids, so every legacy sequential id (F-001…F-083 — 80 live shards) referenced in docs/ produced no doc→feature edge and no DOC_LINK_INTEGRITY validation, while graph-health.ts already carried the correct alternation. src/spec/feature-id.ts is now the single prose-scanning lexer for both sites (fresh RegExp per call — no shared /g state). Simulated on the live repo before implementing: +10 refs gained, all 10 resolve to real shards (0 new warns), hash-id extraction byte-identical, references edges 8→18. spec/_doc-links.yaml regeneration happens via clad sync at branch close. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Hosts send tool_input.file_path ABSOLUTE while moduleOwners keys are the spec's repo-relative posix paths, so buildImpactSlice never resolved and the impact card (F-d6b93648) never rendered in real usage. Measured on cladding-self before the fix: 0/361 module paths hit; after relativizing: 358/361 (99.2% — the 3 misses are trailing-slash directory keys unreachable via the hook). Outside-repo absolute paths degrade to not_found, relative inputs are untouched (idempotent). Card now also displays the relative path. Adds the end-to-end wiring test (runHookEvent with an absolute file_path against a real on-disk spec) that was missing when the bug shipped. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
…caps breaks_if_changed Two seams in one module, landed together deliberately — simulation showed the fan-out alone doubles budget-breach paths (17→33/144 on cladding-self, worst 2.9x the cap), so the clip must ship with it. Fan-out: a module-path query now passes its original form to the iterative impact slice, seeding ALL owners (reverse-slice already supported this) — before, only the alphabetically-first owner's dependents/tests were reported (src/cli/clad.ts: impacted 0 vs 83). Co-owners are seeds, so they surface in co_owners, not impacted; feature-id queries are byte-identical. Clip: breaks_if_changed now participates in the token budget, LAST in the clipping order (needs → code → breaks; the clip-before-code variant simulated strictly worse). Deeper dependents drop from the far end first, then tests outside the depth-1 floor; the direct set is never dropped (must-edit precedent). Fit checks measure WITH the pending 'breaks: omitted …' marker, closing the +3..10-token overshoot the old loops carried. In-budget payloads return byte-identical (pure no-op — existing 6 tests unchanged). Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
…through both sides
Two validity fixes to clad measure, both simulation-verified before coding:
Attribution: medianShrinkFactor was bounded by the 3000-token default budget
— on cladding-self the '~4x smaller' headline was mostly the CAP's
arithmetic, not graph value (uncapped, the structural slice is ~1.16x of
naive: code + structured metadata). The report now splits
fitsCount/truncatedCount with medianShrinkFit/medianShrinkTruncated and a
medianStructuralRatio, and the CLI headline attributes the reduction to the
budget ('budget enforces 3.9x on 163 capped') instead of selling it as
shrink. What the working set actually sells: a guaranteed token budget +
wired needs/breaks/verify context.
One universe: the injected ModuleReader now reaches buildWorkingSet →
codeExcerpt (same safety gates), so the slice and the naive baseline read
the SAME universe — before, a virtual reader fed only the baseline,
silently inflating the shrink factor ('Pure given (spec, reader)' is now
true). The old test asserted the inflated number; rewritten to assert the
honest split.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
…atcher + Host hardening
Body-before-writeHead on every non-SSE route: a mid-write or unparseable
spec throws inside liveGraph(), and committing the 200+application/json
headers first turned that into an HTTP 200 whose body was a prose YAML
error — precisely the state the fs.watch auto-reload refetches into.
Parse failures now answer 503 with a JSON {error} payload (the viewer can
show a retry state); the headersSent guard remains for mid-stream (SSE)
failures. Also covers schema-invalid specs (simulation).
EADDRINUSE: server.on('error') now rejects the boot promise, so
runGraphServeCommand prints one pulse line and exits 1 — before, the
'error' event was unhandled (raw 20-line stack) and the promise never
settled. The listener stays attached so later runtime errors can't crash
the process either. FSWatcher 'error' events degrade to manual refresh
instead of crashing. A foreign Host header is refused (DNS-rebinding
guard; the bind was already 127.0.0.1).
Tests: the old 'watched-file change' test called broadcast() by hand —
renamed to what it tests, and a REAL fs.watch→debounce→SSE chain test
added (writes under spec/, capability-probe skip on platforms without
recursive watch), plus 503, busy-port, and Host-guard cases.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
…; health cache + all-twin badges; live viewer fixes Node identity: the same file materialises as up to three nodes (module:/test:/doc: — 95 paths on cladding-self), and every focus surface picked only the FIRST twin, silently dropping the others' edges. resolveNodeIds returns all twins, subgraph accepts a seed set, and the CLI --focus + clad_get_graph query union them (resolveNodeId keeps the old single-id contract). graph-health now badges ALL twins of a finding's path (first-twin-only left siblings looking healthy) and the viewer's drift pill counts distinct paths so twins can't double the headline number. clad_get_graph: the no-query form returned the whole graph pretty-printed — measured 285KB (~70k tokens) in one MCP result, contradicting the working-set budget discipline. It now answers a graphStats summary (counts by kind + top hubs + a hint at the CLI export, 2.1KB); a focused query still returns the real subgraph. The vacuous clad_get_working_set test (asserted a hand-maintained constant against itself; the handler was never invoked) is replaced with a real MCP round-trip: on-disk module code in must_edit.code, dependent in breaks, budget echo, isError miss. nodeHealth wraps its detector loop in primeSpecCache (the drift.ts run-scope pattern): one shard-tree parse instead of ~10 per /health.json request — measured 611ms → 21ms for the loop, results byte-identical (incl. mtime-sensitive STALE_TESTS on a drifted fixture). Viewer: SSE change detection compares the server's exact graph.json text (baseline = first fetch; never re-serialized client-side) — the old nodes.length proxy missed edge-only and same-count node changes and rendered stale. Wires the dead mobile burger button. CLI: a typo'd --format/--depth now fails loudly instead of silently rendering mermaid. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
…ort(); TS fixtures added
The JS/TS branch missed two statically-extractable forms cladding's own
source uses: 'export … from' re-exports (barrel files are dependencies)
and literal dynamic import('…'). Both now produce edges. A NON-literal
import(expr) flags the file in dynamicImportFiles (kept apart from the
shared DYNAMIC_IMPORT regex — 'import (' would false-flag Python's
parenthesized from-import). The whole JS/TS extraction branch shipped with
zero fixtures (all Python) — a TS fixture set now pins import…from,
side-effect import, require(), re-exports, dynamic import, and the
single-segment ambiguity rule.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
…ee.js license notice
Mermaid ids are deduped (safeId collapsed src/a.ts vs src/a_ts into one
node silently); labels flatten quotes/newlines that broke the quoted-label
syntax. DOT escapes backslashes before quotes. Obsidian wikilink aliases
strip the |[[]] metacharacters that corrupt links. clad_get_events
tolerates a corrupt/partial JSONL line (mid-write tail read) as an
{unparseable} entry instead of crashing the tool call. The viewer bundle
keeps three.js's MIT notice (legalComments 'eof' — 'none' stripped it from
a distributed artifact).
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
…attribution note design.md gains a dated post-ship addendum (§8): the 'no bespoke web UI' decision was reversed (WebGL viewer + live serve shipped, and why the premise no longer applied), clad_get_dependents shipped as clad_get_impact, scope grew (working-set/iterative/infer-deps/measure), and _doc-links.yaml is a grep/human index, not the export source. The 0.7.0 CHANGELOG described the archived 2D prototype (force sliders, Live/Calm, 'no third-party library') — corrected in place with a note, plus an [Unreleased] section for this branch. Glossary's graph row now names html/serve. README no longer claims serve 'opens' the browser. case-efficiency-measurement.md carries the cap-attribution correction (the '4.1×' headline is budget-enforced, not structural). Deprecation notices say 0.8 (0.7.0 shipped every alias while claiming removal in 0.7). Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
… artifacts Updated ACs/test_refs where behavior contracts changed (clad_get_graph no-query summary, working-set fan-out + breaks clipping, measure honest attribution + one-reader universe, hook impact-card absolute-path wiring now pinned end-to-end), each with a Correction note recording why. src/spec/feature-id.ts claimed by the doc-graph feature. clad sync regenerated spec/_doc-links.yaml — the restored legacy F-NNN doc edges (multi-provider-roadmap, ssot-model) plus design.md's addendum references now materialize. npm run build refreshed dist + plugin mirrors + the viewer bundle (SSE text-compare + pill dedupe + burger + three.js MIT notice at EOF). Persona alias deprecation wording aligned to 0.8. Gate: clad check --tier=pre-push --strict GREEN (exit 0), full suite 1681/1681. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Owner
Author
|
Superseded by #225 — identical commits, branch renamed fix/graph-seams → feature/graph-seams to match the feature/* → develop convention. |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
2026-07-02 · fix/graph-seams → developRepairs the seams a deep multi-agent review found in the 0.7.0 graph capability. Every fix was verified by simulation against the real repo before it was coded; the full strict gate is green.
Fixed
▸ The after-edit impact card never fired in real use. Editors send absolute file paths; the index is repo-relative. Hit rate 0/361 → 99.2%, now pinned by an end-to-end test.
▸ Docs referencing older features were invisible to the doc graph. The scanner only recognized the new id format — 10 real references restored, zero new warnings, one shared id lexer so the two scan sites can't diverge again.
▸ Shared files under-reported their blast radius. Asking about a file owned by several features counted only the first owner (a hub file reported 0 impacted instead of the real 83). All owners count now — and the payload still honors its token budget: deeper dependents clip first, the direct set is never dropped, every clip is reported.
▸ The live graph could show a stale picture. Changes that didn't alter the node count never redrew; the page now compares actual content. A broken spec answers 503 with a JSON error instead of 200-with-prose; a busy port fails with one clean line instead of a stack trace; foreign Host headers are refused; the health endpoint parses the spec once instead of ~10× per request (611ms → 21ms for the detector pass).
▸ Three source files were invisible to code review. Raw NUL bytes made git treat them as binary — the graph core shipped with an unreviewable diff. Replaced with escapes (byte-identical at runtime) and guarded by a hygiene test.
▸ One file, one focus. A file that is a module and a test at once got disconnected graph nodes; focus queries and health badges now cover all of a path's nodes.
▸ Assorted robustness: typo'd export options fail loudly, diagram labels escape their special characters, a corrupt log line no longer crashes the events tool, the mobile sidebar button works, and the bundled 3D library keeps its MIT notice.
Changed
▸ Documentation tells the truth about what shipped. The design doc records the "no custom viewer" reversal in a dated addendum; the 0.7.0 changelog section is corrected in place (it described an earlier prototype that never shipped); the efficiency case study carries the budget-attribution correction; deprecation notices say 0.8 (0.7 still shipped every alias).
▸ Dependency inference sees more of what's real. Re-export ("barrel") files and literal dynamic imports now produce edges; non-literal dynamic imports flag the file honestly; the JS/TS extraction path has fixtures for the first time.
Verification
▸ 10 fix designs simulated deterministically against the live repo before implementation (before/after measured; two designs were adjusted by what the simulations found).
▸ Full suite 1681/1681 green ·
clad check --tier=pre-push --strictgreen · live smoke on the built binary (endpoints, 403 host guard, health timing, format validation).🤖 Generated with Claude Code